package org.wu.framework.inner.lazy.orm.spring.aop;

import lombok.NonNull;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Role;
import org.wu.framework.lazy.orm.database.lambda.dynamic.toolkit.DynamicLazyDSContextHolder;
import org.wu.framework.lazy.orm.core.persistence.conf.LazyDynamicEndpoint;
import org.wu.framework.lazy.orm.core.stereotype.LazyMaster;

/**
 * 默认数据源切换
 * <per>
 * <code>
 * LazyDynamicEndpoint dynamicEndpoint = DynamicLazyDSContextHolder.peek();// 当前上下文数据源
 * </code>
 * <code>
 * DynamicLazyDSContextHolder.push(dynamicEndpoint);// 归还数据源
 * </code>
 * <code>
 * DynamicLazyDSContextHolder.clear(); // 清除数据源
 * </code>
 * </per>
 *
 * @see LazyMaster
 */
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class LazyMasterDynamicPointcutAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
    private final Advice advice;

    private final Pointcut pointcut;

    public LazyMasterDynamicPointcutAdvisor(@NonNull LazyClearOtherDBInterceptor lazyDBInterceptor) {
        this.advice = lazyDBInterceptor;
        this.pointcut = buildPointcut();
    }

    private Pointcut buildPointcut() {
        Pointcut cpc = new AnnotationMatchingPointcut(LazyMaster.class, true);
        Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(LazyMaster.class);
        Pointcut classAnnotation = AnnotationMatchingPointcut.forClassAnnotation(LazyMaster.class);
        return new ComposablePointcut(cpc).union(mpc).union(classAnnotation);
    }

    /**
     * Get the Pointcut that drives this advisor.
     */
    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }


    @Override
    public Advice getAdvice() {
        return advice;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

    }

    /**
     * 临时清除上下文数据源
     */
    public static class LazyClearOtherDBInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            // 当前上下文数据源
            LazyDynamicEndpoint dynamicEndpoint = DynamicLazyDSContextHolder.peek();
            try {
                // 清除数据源
                DynamicLazyDSContextHolder.clear();
                return invocation.proceed();
            } finally {
                // 归还数据源
                DynamicLazyDSContextHolder.push(dynamicEndpoint);
            }
        }


    }

}
